package com.xxxx.seckill.controller;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.QueryChainWrapper;
import com.wf.captcha.ArithmeticCaptcha;
import com.xxxx.seckill.pojo.Order;
import com.xxxx.seckill.pojo.SeckillMessage;
import com.xxxx.seckill.pojo.SeckillOrder;
import com.xxxx.seckill.pojo.User;
import com.xxxx.seckill.rabbitmq.MQSender;
import com.xxxx.seckill.service.IGoodsService;
import com.xxxx.seckill.service.IOrderService;
import com.xxxx.seckill.service.ISeckillOrderService;
import com.xxxx.seckill.service_impl.OrderServiceImpl;
import com.xxxx.seckill.utils.JsonUtil;
import com.xxxx.seckill.vo.GoodsVo;
import com.xxxx.seckill.vo.RespBean;
import com.xxxx.seckill.vo.RespBeanEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

//优化前 windows QPS 785
//页面缓存优化后  QPS 1356
//redis预减库存优化 QPS 2454
@Controller
@RequestMapping("/seckill")
@Slf4j
public class SecKillController implements InitializingBean {
@Autowired
    IGoodsService iGoodsService;
@Autowired
    ISeckillOrderService iSeckillOrderService;
@Autowired
    IOrderService iOrderService;
@Autowired
    RedisTemplate redisTemplate;
@Autowired
    MQSender mqSender;

//<商品id，库存是否为空> ,  内存标记
   Map<Long,Boolean> EmptyStockMap=new HashMap<> ();

@RequestMapping(value = "/{path}/doSeckill",method = RequestMethod.POST)
@ResponseBody
    public RespBean doSecKill(@PathVariable String path, User user, Long goodsId){
        if (user==null){
            return RespBean.error (RespBeanEnum.SESSION_ERROR);
        }
//        校验秒杀路径
    ValueOperations valueOperations = redisTemplate.opsForValue ( );
        boolean check=iOrderService.checkPath(user,goodsId,path);
        if (!check){
            return RespBean.error (RespBeanEnum.REQUEST_ILLEGAL);
        }
//        判断是否重复抢购
    SeckillOrder seckillOrder = (SeckillOrder)redisTemplate.opsForValue ( ).get ("order:" + user.getId ( ) + ":" + goodsId);
    if (seckillOrder!=null){

            return RespBean.error (RespBeanEnum.REPEATE_ERROR);
    }

//    通过内存标记减少Redis的访问
    if (EmptyStockMap.get (goodsId)){
            return RespBean.error (RespBeanEnum.EMPTY_STOCK);
    }

//   预减库存
    Long stock = valueOperations.decrement ("seckillGoods:" + goodsId);
    if (stock<0){
        EmptyStockMap.put (goodsId,true);
        valueOperations.increment ("seckillGoods:" + goodsId);
        return RespBean.error (RespBeanEnum.EMPTY_STOCK);
    }
    SeckillMessage seckillMessage = new SeckillMessage (user, goodsId);
    mqSender.sendSeckillMessage (JsonUtil.object2JsonStr (seckillMessage));
    return RespBean.success (0);

//        GoodsVo goodsVoByGoodsId = iGoodsService.findGoodsVoByGoodsId (goodsId);
////        判断库存
//        if (goodsVoByGoodsId.getStockCount ()<1){
//            model.addAttribute ("errmsg", RespBeanEnum.EMPTY_STOCK.getMessage ());
//            return RespBean.error (RespBeanEnum.EMPTY_STOCK);
//        }
////        判断是否重复抢购
////        SeckillOrder one = iSeckillOrderService.getOne (new QueryWrapper<SeckillOrder> ( ).eq ("user_id", user.getId ( ))
////                .eq ("goods_id", goodsId));
//    SeckillOrder seckillOrder = (SeckillOrder)redisTemplate.opsForValue ( ).get ("order:" + user.getId ( ) + ":" + goodsId);
//    if (seckillOrder!=null){
//
//            return RespBean.error (RespBeanEnum.REPEATE_ERROR);
//        }
//       Order order= iOrderService.seckill(user,goodsVoByGoodsId);
//            return RespBean.success (order);

}

//获取秒杀结果
@RequestMapping(value = "/result",method = RequestMethod.GET)
@ResponseBody
public RespBean getResult(User user, Long goodsId){
    if (user==null){
        return RespBean.error (RespBeanEnum.SESSION_ERROR);
    }
    Long Orderid = iSeckillOrderService.getResult(user,goodsId);
    return RespBean.success (Orderid);
}
//获得秒杀路径
@RequestMapping(value = "/path",method = RequestMethod.GET)
@ResponseBody
public RespBean getPath(User user, Long goodsId, String captcha, HttpServletRequest request){
    if (user==null){
        return RespBean.error (RespBeanEnum.SESSION_ERROR);
    }

    ValueOperations valueOperations = redisTemplate.opsForValue ( );

//    设置访问次数，5秒访问5次
    String uri = request.getRequestURI ( );
    Integer count = (Integer) valueOperations.get (uri + ":" + user.getId ( ));
    if (count==null){
         valueOperations.set (uri + ":" + user.getId ( ),1,5,TimeUnit.SECONDS);
    }else if (count<5){
         valueOperations.increment (uri + ":" + user.getId ( ));
    }else {
         return RespBean.error (RespBeanEnum.ACCESS_LIMIT_REAHCED);
    }

    boolean check =iOrderService.checkCaptcha(user,goodsId,captcha);
    if (!check){
        return RespBean.error (RespBeanEnum.ERROR_CAPTCHA);
    }
   String path = iOrderService.createPath(user,goodsId);
    return RespBean.success (path);
}

@RequestMapping(value = "/captcha",method = RequestMethod.GET)
public void verifyCode(User user, Long goodsId, HttpServletResponse response){
    response.setContentType ("image/jpg");
    response.setHeader ("Pargam","No-cache");
    response.setHeader ("Cache-Control","no-cache");
    response.setDateHeader ("Expires",0);
    ArithmeticCaptcha captcha = new ArithmeticCaptcha (130, 32, 3);
    redisTemplate.opsForValue ().set ("captcha:"+user.getId ()+":"+goodsId,captcha.text (),300, TimeUnit.SECONDS);

    try {
        captcha.out (response.getOutputStream ());
    } catch (IOException e) {
        log.error ("验证码生成失败",e.getMessage ());
    }
}

    @Override
    public void afterPropertiesSet() throws Exception {
        List<GoodsVo> list = iGoodsService.findGoodsVo ( );
        if (list==null){
            return;
        }
        list.forEach (goodsVo -> {
            redisTemplate.opsForValue ().set ("seckillGoods:"+goodsVo.getId (),goodsVo.getStockCount ());
            EmptyStockMap.put (goodsVo.getId (),false);
        });
    }
}
